﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Color = System.Drawing.Color;
using Size = System.Drawing.Size;

namespace 修为尽失.辅助.exts
{
    public static class Imaging
    {  public static bool IsTransparentPalette(ColorPalette palette)
        {
            if (palette.Flags != 1 )
                return false;
            int total_colors = palette.Entries.GetLength(0);
            for (int i = 0; i < total_colors - 1; i++)
            {
                if (palette.Entries[i].A != 0)
                {
                    return false;
                }
            }
            return true;
        }

        public static bool HasAlpha(this Bitmap bmp)
        {

            if (IsTransparentPalette(bmp.Palette))
                return true;
                
            for (int i = 0; i < bmp.Height; i++)
            {
                for (int j = 0; j < bmp.Width; j++)
                {
                    var c = bmp.GetPixel(j, i);
                    if (c.A < 255)
                        return true;
                }
            }
            return false;
        }

       
        public  static Rect GetTransparentBounds(this BitmapSource source) {
            var width = source.PixelWidth;
            var height = source.PixelHeight;
            var channel = source.Format.BitsPerPixel / 8;
            var strike = channel * width;
            var pixelBytes = new byte[height * strike];
            source.CopyPixels(pixelBytes, strike, 0);

            double? ax1 = null, ax2 = null, ay1 = null, ay2 = null;
            Rect result = new Rect();
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    byte a = pixelBytes[(y * width + x) * channel + 3];
                    if (a != 255) {
                        if (ax1.HasValue) {
                            ax1 = Math.Min(ax1.Value, x);
                            ax2 = Math.Max(ax2.Value, x);
                        } else {
                            ax1 = x;
                            ax2 = x;
                        }

                        if (ay1.HasValue) {
                            ay1 = Math.Min(ay1.Value, y);
                            ay2 = Math.Max(ay2.Value, y);
                        } else {
                            ay1 = y;
                            ay2 = y;
                        }
                    }
                };
            }

            if(ax1.HasValue){
                result.X = ax1.Value;
                result.Y = ay1.Value;
                result.Width = ax2.Value  - ax1.Value;
                result.Height = ay2.Value - ay1.Value;
            }

            return result;
        }

        public static bool HasAlpha(this BitmapSource source) => !GetTransparentBounds(source).IsEmpty;

        public static int GetJpgSize(Stream stream, out Size JpgSize, out float Wpx, out float Hpx)
        {
            JpgSize = new Size(0, 0);
            Wpx = 0;
            Hpx = 0;
            int rx = 0;
            int ff = stream.ReadByte();
            int type = stream.ReadByte();
            if (ff != 0xff || type != 0xd8)
            {
                //非JPG文件
                stream.Close();
                return rx;
            }

            long ps = 0;
            do
            {
                do
                {
                    ff = stream.ReadByte();
                    if (ff < 0)
//文件结束
                    {
                        stream.Close();
                        return rx;
                    }
                } while (ff != 0xff);

                do
                {
                    type = stream.ReadByte();
                } while (type == 0xff);

                stream.Position.ToString();
                ps = stream.Position;
                switch (type)
                {
                    case 0x00:
                    case 0x01:
                    case 0xD0:
                    case 0xD1:
                    case 0xD2:
                    case 0xD3:
                    case 0xD4:
                    case 0xD5:
                    case 0xD6:
                    case 0xD7:
                        break;
                    case 0xc0:
//SOF0段
                        ps = stream.ReadByte() * 256;
                        ps = stream.Position + ps + stream.ReadByte() - 2; //加段长度
                        stream.ReadByte();
//丢弃精度数据
                        //高度
                        JpgSize.Height = stream.ReadByte() * 256;
                        JpgSize.Height = JpgSize.Height + stream.ReadByte();
                        //宽度
                        JpgSize.Width = stream.ReadByte() * 256;
                        JpgSize.Width = JpgSize.Width + stream.ReadByte();

//后面信息忽略
                        if (rx != 1 && rx < 3)
                            rx = rx + 1;
                        break;
                    case 0xe0:
//APP0段
                        ps = stream.ReadByte() * 256;
                        ps = stream.Position + ps + stream.ReadByte() - 2; //加段长度
                        stream.Seek(5, SeekOrigin.Current);
//丢弃APP0标记(5bytes)
                        stream.Seek(2, SeekOrigin.Current);
//丢弃主版本号(1bytes)及次版本号(1bytes)
                        int units = stream.ReadByte();
//X和Y的密度单位,units=0：无单位,units=1：点数/英寸,units=2：点数/厘米

                        //水平方向(像素/英寸)分辨率
                        Wpx = stream.ReadByte() * 256;
                        Wpx = Wpx + stream.ReadByte();
                        if (units == 2)
                            Wpx = (float) (Wpx * 2.54); //厘米变为英寸

//垂直方向(像素/英寸)分辨率
                        Hpx = stream.ReadByte() * 256;
                        Hpx = Hpx + stream.ReadByte();
                        if (units == 2)
                            Hpx = (float) (Hpx * 2.54); //厘米变为英寸

//后面信息忽略
                        if (rx != 2 && rx < 3)
                            rx = rx + 2;
                        break;
                    default:
//别的段都跳过////////////////
                        ps = stream.ReadByte() * 256;
                        ps = stream.Position + ps + stream.ReadByte() - 2; //加段长度
                        break;
                }

                if (ps + 1 >= stream.Length)
//文件结束
                {
                    stream.Close();
                    return rx;
                }

                stream.Position = ps; //移动指针
            } while (type != 0xda); // 扫描行开始

            stream.Close();
            return rx;
        }

        public static string ToBase64String(this Image img, bool close = true)
        {
            return Convert.ToBase64String(ToBytes(img, close));
        }

        public static byte[] ToBytes(this Image img, bool close = true)
        {
            var ms = new MemoryStream();
            img.Save(ms, ImageFormat.Png);
            var bs = ms.ToArray();
            if (close)
                ms.Close();
            return bs;
        }

        public static string ToBase64StringWithoutFormat(this BitmapSource bmp)
        {
            var strike = (bmp.PixelWidth * bmp.Format.BitsPerPixel) / 8;
            var arr = new byte[strike * bmp.PixelHeight];
            bmp.CopyPixels(arr, strike, 0);
            return Convert.ToBase64String(arr);
        }

        public static string ToBase64String(this BitmapSource bmp, bool close = true)
        {
            return Convert.ToBase64String(ToBytes(bmp, close));
        }

        public static byte[] ToBytes(this BitmapSource src, bool close = true)
        {
            var ms = ToMemoryStream(src);
            var b = ms.ToArray();
            if (close)
                ms.Close();
            return b;
        }

        public static MemoryStream ToMemoryStream(this BitmapSource src)
        {
            var stream = new MemoryStream();
            var e = new PngBitmapEncoder();
            e.Frames.Add(BitmapFrame.Create(src));
            e.Save(stream);
            return stream;
        }

        public static Bitmap ToBitmap(this BitmapSource src, System.Drawing.Imaging.PixelFormat formatto)
        {
            var bmp = new Bitmap(src.PixelWidth, src.PixelHeight, formatto);
            try
            {
                var data = bmp.LockBits(new Rectangle(System.Drawing.Point.Empty, bmp.Size), ImageLockMode.WriteOnly, formatto);
                src.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
                bmp.UnlockBits(data);
            }
            catch
            {
                return null;
            }

            return bmp;
        }

        public static Bitmap ToBitmap(this BitmapImage src)
        {
            if (src.UriSource.IsAbsoluteUri && src.UriSource.IsFile)
            {
                return new Bitmap(src.UriSource.OriginalString);
            }

            return new Bitmap(ToMemoryStream(src));
        }

        public static BitmapImage ToBitmapImage(this Bitmap bitmap,bool dispose=true)
        {
            var bitmapImage = new BitmapImage();
            using (var ms = new MemoryStream())
            {
                bitmap.Save(ms, ImageFormat.Png);
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = ms;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
            }
            if (dispose)
                bitmap.Dispose();
            return bitmapImage;
        }

        public static BitmapSource ToBitmapSource(this Bitmap bmp, bool dispose = true)
        {
            var ptr = bmp.GetHbitmap();
            var bitmapsource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ptr, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(bmp.Width, bmp.Height));
            NativeMethods.DeleteObject(ptr);
            if (dispose)
                bmp.Dispose();
            return bitmapsource;
        }

        public static ImageSource CreateImageSourceFromImage(this Bitmap image)
        {
            if (image == null)
                return null;
            try
            {
                var hbitmap = image.GetHbitmap();
                var sourceFromHbitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                sourceFromHbitmap.Freeze();
                image.Dispose();
                NativeMethods.DeleteObject(hbitmap);
                return sourceFromHbitmap;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        public static ImageSource CreateImageSourceThumbnail(string fileName, double width, double height)
        {
            var original = Image.FromFile(fileName);
            var rate = original.Width / (float) original.Height;
            if (width.Is0OrNaN())
                width = height * rate;
            if (height.Is0OrNaN())
                height = width / rate;
            var width1 = (int) width;
            var height1 = (int) height;
            var bitmap = new Bitmap(original, width1, height1);
            var hbitmap = bitmap.GetHbitmap();
            var sourceFromHbitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            sourceFromHbitmap.Freeze();
            NativeMethods.DeleteObject(hbitmap);
            original.Dispose();
            bitmap.Dispose();
            return sourceFromHbitmap;
        }

        public static ImageSource CreateImageSourceThumbnail(this Image sourceImage, double width, double height)
        {
            if (sourceImage == null)
                return null;
            var num = (float) Math.Min(width / sourceImage.Width, height / sourceImage.Height);
            var width1 = sourceImage.Width;
            var height1 = sourceImage.Height;
            if (num < 1.0)
            {
                width1 = (int) Math.Round(sourceImage.Width   * ((double) num));
                height1 = (int) Math.Round(sourceImage.Height * ((double) num));
            }

            var bitmap = new Bitmap(sourceImage, width1, height1);
            var hbitmap = bitmap.GetHbitmap();
            var sourceFromHbitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            sourceFromHbitmap.Freeze();
            NativeMethods.DeleteObject(hbitmap);
            bitmap.Dispose();
            return sourceFromHbitmap;
        }

        public static ImageSource CreateImageSourceThumbnail(byte[] data, double width, double height)
        {
            using (var stream = (Stream) new MemoryStream(data, true))
            {
                using (var sourceImage = Image.FromStream(stream)) return CreateImageSourceThumbnail(sourceImage, width, height);
            }
        }

        public static ImageSource CreateRandomImageSource(int w, int h)
        {
            var ran = new Random(Guid.NewGuid().GetHashCode());
            var pf = PixelFormats.Bgr32;
            var rawStride = ((w * pf.BitsPerPixel) + 7) / 8;
            var rawImage = new byte[rawStride * h];
            ran.NextBytes(rawImage);
            return BitmapSource.Create(w, h, 96, 96, pf, null, rawImage, rawStride);
        }

        public static Image CreateThumbnail(string filename, int width, int height)
        {
            Bitmap bitmap;
            try
            {
                bitmap = new Bitmap(filename);
            }
            catch
            {
                bitmap = new Bitmap(width, height);
            }

            return CreateThumbnail(bitmap, width, height, System.Drawing.Color.Transparent, false);
        }

        public static Image CreateThumbnail(Stream fileStream, int width, int height, Color borderColor)
        {
            Bitmap bitmap;
            try
            {
                bitmap = new Bitmap(fileStream);
            }
            catch
            {
                bitmap = new Bitmap(width, height);
            }

            return CreateThumbnail(bitmap, width, height, borderColor);
        }

        public static Image CreateThumbnail(string fileName, int width, int height, Color borderColor)
        {
            Bitmap bitmap;
            try
            {
                bitmap = new Bitmap(fileName);
            }
            catch
            {
                bitmap = new Bitmap(width, height);
            }

            return CreateThumbnail(bitmap, width, height, borderColor);
        }

        public static Image CreateThumbnail(this Image image, int width, int height, Color borderColor)
        {
            Bitmap bitmap;
            try
            {
                bitmap = new Bitmap(image);
            }
            catch
            {
                bitmap = new Bitmap(width, height);
            }

            return CreateThumbnail(bitmap, width, height, borderColor);
        }

        public static Image CreateThumbnail(this Bitmap bitmap, int width, int height, Color borderColor, bool hasborder = true)
        {
            width = (bitmap.Width   > width) ? width : bitmap.Width;
            height = (bitmap.Height > height) ? height : bitmap.Height;
            var bitmap1 = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format64bppPArgb);
            using (var graphics = Graphics.FromImage(bitmap1))
            {
                var width1 = width;
                var height1 = height;
                if (bitmap.Width > bitmap.Height)
                    height1 = (int) ((bitmap.Height / ((double) bitmap.Width)) * width1);
                else if (bitmap.Width < bitmap.Height)
                    width1 = (int) ((bitmap.Width / ((double) bitmap.Height)) * height1);
                var x = (width  / 2) - (width1  / 2);
                var y = (height / 2) - (height1 / 2);
                graphics.PixelOffsetMode = PixelOffsetMode.None;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.DrawImage(bitmap, x, y, width1, height1);
                if (hasborder)
                {
                    using (var pen = new System.Drawing.Pen(borderColor, 1f)) graphics.DrawRectangle(pen, 0, 0, bitmap1.Width - 1, bitmap1.Height - 1);
                }

                graphics.Dispose();
                return bitmap;
            }
        }
    }

    /// <summary>
    /// 图片处理类
    /// 1、生成缩略图片或按照比例改变图片的大小和画质
    /// 2、将生成的缩略图放到指定的目录下
    /// </summary>
    public class ImageClass
    {
        public Image ResourceImage;
        private int ImageWidth;
        private int ImageHeight;
        public string ErrMessage;

        /// <summary>
        /// 类的构造函数
        /// </summary>
        /// <param name="ImageFileName">图片文件的全路径名称</param>
        public ImageClass(string ImageFileName)
        {
            ResourceImage = Image.FromFile(ImageFileName);
            ErrMessage = "";
        }

        public bool ThumbnailCallback()
        {
            return false;
        }

        /// <summary>
        /// 生成缩略图重载方法1，返回缩略图的Image对象
        /// </summary>
        /// <param name="Width">缩略图的宽度</param>
        /// <param name="Height">缩略图的高度</param>
        /// <returns>缩略图的Image对象</returns>
        public Image GetReducedImage(int Width, int Height)
        {
            try
            {
                Image ReducedImage;
                Image.GetThumbnailImageAbort callb = new Image.GetThumbnailImageAbort(ThumbnailCallback);
                ReducedImage = ResourceImage.GetThumbnailImage(Width, Height, callb, IntPtr.Zero);
                return ReducedImage;
            }
            catch (Exception e)
            {
                ErrMessage = e.Message;
                return null;
            }
        }

        /// <summary>
        /// 生成缩略图重载方法2，将缩略图文件保存到指定的路径
        /// </summary>
        /// <param name="Width">缩略图的宽度</param>
        /// <param name="Height">缩略图的高度</param>
        /// <param name="targetFilePath">缩略图保存的全文件名，(带路径)，参数格式：D:\Images\filename.jpg</param>
        /// <returns>成功返回true，否则返回false</returns>
        public bool GetReducedImage(int Width, int Height, string targetFilePath)
        {
            try
            {
                Image ReducedImage;
                Image.GetThumbnailImageAbort callb = new Image.GetThumbnailImageAbort(ThumbnailCallback);
                ReducedImage = ResourceImage.GetThumbnailImage(Width, Height, callb, IntPtr.Zero);
                ReducedImage.Save(@targetFilePath, ImageFormat.Jpeg);
                ReducedImage.Dispose();
                return true;
            }
            catch (Exception e)
            {
                ErrMessage = e.Message;
                return false;
            }
        }

        /// <summary>
        /// 生成缩略图重载方法3，返回缩略图的Image对象
        /// </summary>
        /// <param name="Percent">缩略图的宽度百分比 如：需要百分之80，就填0.8</param>  
        /// <returns>缩略图的Image对象</returns>
        public Image GetReducedImage(double Percent)
        {
            try
            {
                Image ReducedImage;
                Image.GetThumbnailImageAbort callb = new Image.GetThumbnailImageAbort(ThumbnailCallback);
                ImageWidth = Convert.ToInt32(ResourceImage.Width  * Percent);
                ImageHeight = Convert.ToInt32(ResourceImage.Width * Percent);
                ReducedImage = ResourceImage.GetThumbnailImage(ImageWidth, ImageHeight, callb, IntPtr.Zero);
                return ReducedImage;
            }
            catch (Exception e)
            {
                ErrMessage = e.Message;
                return null;
            }
        }

        /// <summary>
        /// 生成缩略图重载方法4，返回缩略图的Image对象
        /// </summary>
        /// <param name="Percent">缩略图的宽度百分比 如：需要百分之80，就填0.8</param>  
        /// <param name="targetFilePath">缩略图保存的全文件名，(带路径)，参数格式：D:\Images\filename.jpg</param>
        /// <returns>成功返回true,否则返回false</returns>
        public bool GetReducedImage(double Percent, string targetFilePath)
        {
            try
            {
                Image ReducedImage;
                Image.GetThumbnailImageAbort callb = new Image.GetThumbnailImageAbort(ThumbnailCallback);
                ImageWidth = Convert.ToInt32(ResourceImage.Width  * Percent);
                ImageHeight = Convert.ToInt32(ResourceImage.Width * Percent);
                ReducedImage = ResourceImage.GetThumbnailImage(ImageWidth, ImageHeight, callb, IntPtr.Zero);
                ReducedImage.Save(@targetFilePath, ImageFormat.Jpeg);
                ReducedImage.Dispose();
                return true;
            }
            catch (Exception e)
            {
                ErrMessage = e.Message;
                return false;
            }
        }
    }
}